{ Esta unidad se encargar de contener la clase y los objetos que forman la solución.
 Una solución está compuesta por una lista de proyectos, los cuales a su vez contienen
 una lista de ficheros.

  Los procedimientos que guardan datos los hacen  formato XML, el código
  original es de aquí: http://wiki.lazarus.freepascal.org/XML_Tutorial

  Copyright (C) 2010 Jorge Turiel jorgeturiel@gmail.com

  This source is free software; you can redistribute it and/or modify it under
  the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your option)
  any later version.

  This code is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  details.

  A copy of the GNU General Public License is available on the World Wide Web
  at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  MA 02111-1307, USA.
}

unit uSolucion;

{$mode objfpc}{$H+}

interface
uses
  Classes, SysUtils,DOM, XMLWrite,XMLRead,Strings,uUtiles;

Type TFichero = Record
     Nombre : String; //Nombre del fichero
     idTab  : Integer; //Tag, del Tabsheet que lo contiene, -1 sino tiene ninguno
     Compilar : boolean; //indica si el fichero es compilable o no
end;

Type

{ TProyecto }

TProyecto = class//(TObject)
private

     Public
           Nombre : String;
           Ruta : String; //Contiene la ruta dónde se guarda el fichero del proyecto
           Modificado : Boolean;
           Ficheros : Array of TFichero;
           Placa : String; //Este proyecto es para este tipo de placa.
           Constructor Create;
           Constructor Create (NombreProyecto : String);OverLoad;
           Constructor Create (stNombre:String;stFichero:String;nTab : Integer);OverLoad;
           Constructor Create (stNombre:String;stFichero:String;
                               stRuta : String;nTab : Integer);OverLoad;
           Procedure BorrarFichero (Indice : Integer);
           Function AddFichero (Fichero : TFichero):boolean;  //Añade un fichero
           Procedure Guardar (stRuta,stNombre : String);
           Procedure Cargar (stRuta : String);
           Function Contar: Integer;
           Procedure GuardarFichero (aFichero : TFichero; stRuta : String);
           {Devuelve el Id del fichero pasado por parámetro.
             Parámetros:
                         stFichero : String; Nombre del Fichero del que se quiere obtener el ID
             Devuelve:
                       Un entero del id del fichero, en el caso de no encontrarlo devuelve -1}
           function idFichero (stFichero:String):Integer;

end;
Type

{ TSolucion }

TSolucion = class// (TObject)
private
     Private
           Function ExisteFichero (NombreProyecto : String; NombreFichero : String):Boolean;
           Procedure Cargar  (stRuta : String);
     Public
     Nombre : string;
     Ruta : String; //Ruta dónde se guarda esta solución
     Proyectos : Array of TProyecto;
     Modificado : Boolean;
     Constructor Create;
     Constructor Create (Proyecto : TProyecto);OverLoad;
     Constructor Create (stRuta : String);OverLoad;
     //Añade un proyecto a la solución actual
     Function AddProyecto (Var aProyecto : TProyecto):Boolean;
     {Guarda la solución actual en la ruta indicada por el parámetro}
     Procedure Guardar (stRuta : String);
     {Borrar el proyecto indicado por el parámetro }
     Procedure BorrarProyecto (Indice : Integer);
     Function Contar:Integer;
     Function ExisteProyecto (aNombre : String):Boolean;
     Function ProyectoPorNombre(stNombre: String): TProyecto;
     //Añade un fichro al proyecto indicado
     Function AddFicheroAlProyecto (NombreProyecto: String;  Fichero : TFichero) : Boolean;
     Function IndexOf (stNombreProyecto : string):Integer;
     {Devuelve el idTag de un fichero determinado del proyecto indicado
      Parámetro:
                 stNombreProyecto : String; Nombre del proyecto que contiene el archivo a buscar
                 stNombreArchivo : String; Nombre del archivo a buscar;

      Devuelve:
                Un número mayor de -1 en el caso de que lo encuentre, en caso contrario
                devuelve -1;
     }
     Function idTagArchivo (stNombreProyecto:String;stNombreArchivo:String): Integer;

end;
Const
    Ext : string = '.pde';
    ExtSolucion : String ='.sol';
    ExtProyecto : String = '.prt';
Var
    //La solución
    Solucion : TSolucion;
implementation
Uses uEditorComun,uComun;
{TSolucion}
constructor TSolucion.Create (Proyecto: TProyecto );
Begin
   //Borrar;
   Nombre := Proyecto.Nombre;
   SetLength (Proyectos,0);
   AddProyecto (Proyecto);
   If (Proyecto.Ruta <> '') and (Not (DirectoryExists (Proyecto.ruta)))Then
   Begin
    CreateDir (Proyecto.ruta)
   end;
end;

constructor TSolucion.Create(stRuta: String);

var
  P: TProyecto;
  F : TFichero;
  xdoc: TXMLDocument;
  NodoPadre: TDOMNode;
  NodoTEmp: TDOMNode;
  NombrePRoyecto: WideString;
  RutaProyecto: WideString;
  Dimension: Integer;
  I: Integer;
  Actual : Boolean;
begin
   SetLength (Proyectos,0);
   Nombre := ExtraerExtension (ExtractFileName (stRuta));
  Ruta := ExtractFilePath (stRuta);
  ReadXMLFile(xdoc,stRuta);


  For I:= 0 to xdoc.DocumentElement.ChildNodes.Count-1 do
  Begin
      P := TProyecto.Create;
      NodoPadre := xDoc.DocumentElement.ChildNodes[i];
      NodoTEmp := NodoPadre.Attributes.GetNamedItem('Nombre');
      NombrePRoyecto := nodotemp.TextContent;
      RutaProyecto:=NodoPAdre.ChildNodes[0].TextContent;
      Actual := StrToBool (NodoPadre.ChildNodes[1].TextContent);
      P.Cargar(RutaProyecto+NombreProyecto+ExtProyecto);
      P.Nombre:=NombreProyecto;

      Dimension := Length (Proyectos);
      SetLength (Proyectos,Dimension+1);
      Proyectos[Dimension] :=Tproyecto(P);

      If Actual Then
      Begin
       ProyectoActual := NombreProyecto;
      end;
      P := nil;
  end;
   Xdoc.free;
   MuestraMensaje (General,Proyectos[0].Nombre);
end;

//No puede haber dos proyectos iguales
Function TSolucion.AddProyecto (Var aProyecto: TProyecto ):Boolean;
Var
    Dimension :integer;
    I : Integer;
    Existe : Boolean;
    Registro : TProyecto;

begin
 //Result := False;
 //I := 0;
 //Existe := False;
 //Registro := TProyecto.Create;
 //While I < Length (Proyectos) do
 //Begin
 //    Registro := Proyectos[i];
 //    If StriComp (pchar(Registro.Nombre),pchar(aProyecto.Nombre)) = 0 Then
 //    Begin
 //     Existe := True;
 //     I := Length (Proyectos);
 //    end;
 //    Inc(I);
 //    Registro := nil;
 //end;
 Existe := False;
 If Not Existe Then
 Begin
  Dimension := Length (Proyectos);
  SetLength (Proyectos,Dimension+1);
  Proyectos [Dimension] := aProyecto;
  Modificado := True;
  Result := True;
 end;
 For I := 0 to Length (aProyecto.Ficheros)-1 do
 Begin
     MuestraMensaje (Editor,'--'+aProyecto.Ficheros[i].nombre);
 end;
 //If Registro <> nil Then
 // FreeAndNil (Registro);
end;

procedure TSolucion.Guardar(stRuta: string);
var
  xDoc: TXMLDocument;
  Proyecto: TProyecto;
  RootNode: TDOMElement;
  I: Integer;
  ParentNode: TDOMElement;
  NodoHijo: TDOMText;
begin
   xDoc := TXMLDocument.create;
   RootNode := xDoc.CreateElement('Solucion');
   xDoc.AppendChild(RootNode);
   Proyecto := TProyecto.Create('','',-1);
   For I:= 0 To Solucion.Contar-1 do
   Begin
       Proyecto := Proyectos[i];
       RootNode := xDoc.DocumentElement;
       ParentNode := xDoc.CreateElement('Proyecto');
       TDOMElement(ParentNode).SetAttribute('Nombre',Proyecto.Nombre);
       RootNode.AppendChild(ParentNode);


       ParentNode := xDoc.CreateElement('Ruta');
       NodoHijo := xDoc.CreateTextNode(Proyecto.Ruta);
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[I].AppendChild(ParentNode);

       ParentNode := xDoc.CreateElement('Actual');
       NodoHijo := xDoc.CreateTextNode(BoolToStr (Proyecto.Nombre = ProyectoActual));
       ParentNode.AppendChild(NodoHijo);

       RootNode.ChildNodes.Item[I].AppendChild(ParentNode);
       //Guarda el proyecto
       Proyecto.Guardar(Proyecto.Ruta,Proyecto.Nombre);
       Proyecto := nil;
   end;
   IF Proyecto <> nil Then
   Begin
     Proyecto := nil;
   end;
   IF Not DirectoryExists  (ExtractFilePath(stRuta)) Then
   Begin
    CreateDir (ExtractFilePath(stRuta));
   end;
   WriteXMLFile (xdoc,stRuta+ExtSolucion);
   xdoc.free;

end;
 { TODO : Pendiente de Probar }
procedure TSolucion.Cargar (stRuta: String );
Var
   j: Integer;
    I : Integer;
    NodoPadre : TDOMNode;
    ProyectoACargar : TProyecto;
    xDoc : TXMLDocument;
    NombreProyecto,
    RutaProyecto : string;
    nodotemp: TDOMNode;
    Dimension: Integer;
    B: WideString;
begin
  ReadXMLFile(xdoc,stRuta);
  SetLength (Proyectos,0);

  For I:= 0 to xdoc.DocumentElement.ChildNodes.Count-1 do
  Begin
      NodoPadre := xDoc.DocumentElement.ChildNodes[i];
      NodoTEmp := NodoPadre.Attributes.GetNamedItem('Nombre');
      NombrePRoyecto := nodotemp.TextContent;
      RutaProyecto:=NodoPAdre.ChildNodes[0].TextContent;

      B := NodoPadre.ChildNodes[1].TextContent;

      ProyectoACargar := TProyecto.Create (NombreProyecto);

      ProyectoACargar.Cargar(RutaProyecto+NombreProyecto+ExtProyecto);



      Dimension := Length (Proyectos);
      SetLength (Proyectos,Dimension+1);
      Proyectos [Dimension] := ProyectoACargar;
      Modificado := True;
      For j := 0 To length (ProyectoACargar.Ficheros)-1 do
      Begin
          MuestraMensaje (Editor,ProyectoACargar.Ficheros[j].nombre);
      end;
     If ProyectoACargar <> nil Then
       FreeAndNil(ProyectoACargar);
  end;
  Ruta := ExtractFilePath (stRuta);

  If ProyectoACargar <> nil Then
     FreeAndNil(ProyectoACargar);
  xdoc.free;

end;

constructor TSolucion.Create;
begin

end;

procedure TSolucion.BorrarProyecto (Indice: Integer );
Var
   ALength : Integer;
   TailElements : Cardinal;
Begin
   ALength := Length(Proyectos);
   Assert(ALength > 0);
   Assert(Indice < ALength);
   Finalize(Proyectos[Indice]);
   TailElements := ALength - Indice;
   if TailElements > 0 then
   Move(Proyectos[Indice + 1], Proyectos[Indice], SizeOf(TProyecto) * TailElements);
   Initialize(Proyectos[ALength - 1]);
   SetLength(Proyectos, ALength - 1);
   Modificado := True;
end;



function TSolucion.Contar: Integer;
begin
  Result := Length (Proyectos);
end;

function TSolucion.ExisteProyecto(aNombre: String): Boolean;
var
  I: Integer;
  n: String;
begin
  I:= 0;
  Result := False;
  While I < Length (Proyectos) do
  Begin
  n := Proyectos [i].Nombre;
   If UpperCase(aNombre) = UpperCase (Proyectos [i].Nombre) Then
   Begin
    Result := True;
    I := Length (Proyectos);
   end;
   Inc(i);
  end;
end;

function TSolucion.ProyectoPorNombre(stNombre: String): TProyecto;
Var
   I :Integer;
begin
  Result := nil;
  I :=0;
  While i < Length (Proyectos) Do
  Begin
   If Proyectos[I].Nombre = stNombre Then
   Begin
      Result := Proyectos[i];
      I := Length (Proyectos);
   end;
   Inc(i);
  end;
end;

function TSolucion.AddFicheroAlProyecto(NombreProyecto: String;
   Fichero: TFichero): Boolean;
var
  Indice: LongInt;
begin
  Result := False;

  //Existe el proyecto
  Indice := IndexOf (NombreProyecto);
  IF Indice < 0 Then
  Begin
     Exit;
  end;

  IF Proyectos[indice].AddFichero(Fichero) = True Then
  Begin
     Result := True;
  end;



end;

function TSolucion.IndexOf(stNombreProyecto : String): Integer;
Var
  i : Integer;
begin
 Result := -1;
 I := 0;
 While I < Length (Proyectos) do
 Begin
  If Proyectos[i].Nombre = stNombreProyecto Then
     Begin
     Result := I;
     i := Length (Proyectos);
     end;
  Inc(I);
  end;

end;

function TSolucion.idTagArchivo(stNombreProyecto: String;
  stNombreArchivo: String): Integer;
var
  ProyectoABuscar: TProyecto;
begin
   Result := -1;
   If Not ExisteProyecto(stNombreProyecto) Then Exit;
   ProyectoABuscar := TProyecto.Create;
   ProyectoABuscar := ProyectoPorNombre(stNombreProyecto);
   Result := ProyectoABuscar.idFichero(stNombreArchivo);
end;

function TSolucion.ExisteFichero(NombreProyecto: String;
                                 NombreFichero: String): Boolean;
var
  I : Integer;
  j : Integer;
begin
 Result := False;
 I := 0;
 While I < Length (Proyectos) Do
 Begin
  IF Proyectos[i].Nombre = NombreProyecto Then
  Begin
     J := 0;
     While J < Length (Proyectos[i].Ficheros) do
     Begin
      IF Proyectos[i].Ficheros[j].Nombre = NombreFichero Then
      Begin
         Result := True;
         j := Length (Proyectos[i].Ficheros);
         I := Length (Proyectos);
      end;
      Inc(j);
     end;
     Inc(i);
  end;
 end;
end;


{TProyecto}
//Todo: Añadir tipo de Arduino cuando esté en el TProyecto

constructor TProyecto.Create;
begin
   inherited;
end;

constructor TProyecto.Create(NombreProyecto: String);
begin
  Nombre := NombreProyecto;
  SetLength (Ficheros,0);
end;

constructor TProyecto.Create(stNombre: String; stFichero: String; nTab: Integer);
Var
  FicheroNuevo : TFichero;
Begin
  SetLength (Ficheros,1);
  FicheroNuevo.Nombre:=stFichero;
  FicheroNuevo.Compilar:= True;
  FicheroNuevo.idTab:= nTab;
  Nombre := stNombre;
  Ruta := '';
  Modificado := True;
  Ficheros[0] := FicheroNuevo;
end;

constructor TProyecto.Create(stNombre: String; stFichero: String;
  stRuta: String; nTab: Integer);
Var
  FicheroNuevo : TFichero;
Begin
  SetLength (Ficheros,1);
  FicheroNuevo.Nombre:=stFichero;
  FicheroNuevo.Compilar:= True;
  FicheroNuevo.idTab:= nTab;
  Nombre := stNombre;
  Ruta := '';
  Modificado := True;
  Ficheros[0] := FicheroNuevo;
  BorrarDirectorioLleno (stRuta);
end;

//Borra un fichero de la lista
//Original de: http://pages.cs.wisc.edu/~rkennedy/array-delete

Procedure TProyecto.BorrarFichero (Indice : Integer);
Var
   ALength : Integer;
   TailElements : Cardinal;
Begin

   ALength := Length(Ficheros);
   Assert(ALength > 0);
   Assert(Indice < ALength);
   Finalize(Ficheros[Indice]);
   TailElements := ALength - Indice;
   if TailElements > 0 then
   Move(Ficheros[Indice + 1], Ficheros[Indice], SizeOf(TFichero) * TailElements);
   Initialize(Ficheros[ALength - 1]);
   SetLength(Ficheros, ALength - 1);
   Modificado := True;
end;
//No puede haber dos ficheros con el mismo nombre
Function TProyecto.AddFichero (Fichero: TFichero ):Boolean;
Var
  Dimension : Integer;
  I : Integer;
  Existe : Boolean;
  Registro: TFichero;

begin
   Result := False;
   Existe := False;
   I:= 0;
   While I < Length (Ficheros) Do
   Begin
       Registro := Ficheros[i];
       If StrIComp (pChar(Registro.Nombre),pChar(Fichero.Nombre)) = 0 then
       begin
        Existe := true;
        i := Length (Ficheros);
       end;
    Inc (i);
   end;

   If Not Existe Then
   Begin
    Dimension := Length (Ficheros);
    SetLength (Ficheros,Dimension+1);
    Ficheros[Dimension] := Fichero;
    Modificado := True;
    Result := True;
   end;
end;
//Todo: Debe Comprobar si hay algún Fichero pendiente de guardar.
//Una posibilidad es crear una rutina que devuelva el primer fichero
//Que encuentre que se debe modificar, y este procedimiento hacerlo privado
Procedure TProyecto.Guardar (stRuta,stNombre : String);
var
  xdoc: TXMLDocument;
  RootNode: TDOMElement;
  NodoHijo: TDOMText;
  ParentNode: TDOMElement;
  I: Integer;
  Registro: TFichero;
  FicheroActual: TFichero;
  r: Boolean;
Begin

   //Crear documento XMl
   xdoc := TXMLDocument.create;
   //Crear nodo raiz.
   RootNode := xdoc.CreateElement('Proyecto');
   xdoc.AppendChild(RootNode);
   For I:= 0 To Length (Ficheros)-1 Do
   Begin
       Registro := Ficheros[i];
       //Crear un nodo padre
       RootNode := xdoc.DocumentElement;
       ParentNode := xdoc.CreateElement('Fichero');
       TDOMElement(ParentNode).SetAttribute('ID',IntToStr (I));
       RootNode.AppendChild(ParentNode);

       //Crear un nodo hijo
       ParentNode := xdoc.CreateElement('Nombre');
       NodoHijo := xdoc.CreateTextNode(Registro.Nombre);
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);

       //Crear Hijo
       ParentNode := xDoc.CreateElement('Idtab');
       NodoHijo := xdoc.CreateTextNode(IntToStr (Registro.idTab));
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);

       //Crear Hijo
       ParentNode := xDoc.CreateElement('Compilar');
       NodoHijo := xdoc.CreateTextNode(BoolToStr (Registro.Compilar));
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);
   end;

   //Guardar Fichero XML
   If DirectoryExists (stRuta) Then
   Begin
    BorrarDirectorioLleno (stRuta);
   end;
   r := CreateDir(stRuta);
   WriteXMLFile (xdoc,stRuta+stNombre+ExtProyecto);
   Modificado := False;
  // Ruta := ExtractFilePath (stRuta);
   xdoc.free;
   //Guarda Ficheros del proyecto
   For I:= 0 to Length (Ficheros)-1 do
   Begin
       FicheroActual := Ficheros[i];
       GuardarFichero (FicheroActual,stRuta);
   end;
end;

procedure TProyecto.Cargar (stRuta: String );
Var
   xdoc : TXMLDocument;
   I: Integer;
   NodoPadre: TDOMNode;
   Registro: TFichero;
begin
 ReadXMLFile (xdoc,stRuta);
 SetLength (Ficheros,0);

 Ruta := (stRuta);
 For I:= 0 To xdoc.DocumentElement.ChildNodes.Count-1 do
 Begin
     NodoPadre := xdoc.DocumentElement.ChildNodes[i];
     Registro.Nombre:= NodoPadre.ChildNodes[0].TextContent;
     Registro.idTab:= StrToInt (NodoPadre.ChildNodes[1].TextContent);
     Registro.Compilar:=StrToBool(NodoPadre.ChildNodes[2].TextContent);
     AddFichero (Registro);
 end;
 Modificado := False;
 xdoc.free;
end;

function TProyecto.Contar: Integer;
begin

  Result := Length (Ficheros);
  //Result := 0;

end;

procedure TProyecto.GuardarFichero(aFichero: TFichero; stRuta: String);
begin
  If aFichero.idTab < 1 Then Exit;
  //Localizar la pestaña
  GuardarEditor (aFichero.idTab,stRuta+aFichero.Nombre);
end;

function TProyecto.idFichero(stFichero: String): Integer;
Var
   I : Integer;
begin
  Result := -1;
  I := 0;
  While I < Length(Ficheros) do
  Begin
    If Ficheros[I].Nombre = stFichero Then
    Begin
      Result := Ficheros[i].idTab;
      i := Length(Ficheros);
    end;
    Inc(I);
  end;
end;

end.

